/*
 * File:   driver_led.c
 * Author: Ich
 *
 * Created on 23.02.2014, 16:03
 *
 */



#include <plib.h>
#include "driver_led.h"
#include "driver_timer.h"
#include "config_dma.h"

/*******************************************************************************
 *  private preprocessing definitions
 */
#define NOF_LED_EMPTYLEDS (2)   // after setting the LED, some time (50usec) is required for sending nothing -> empty led
#define SIZE_LED          (DRVLED_MAX_LED+NOF_LED_EMPTYLEDS)
/*******************************************************************************
 *  private variables
 */
static const UINT16 DMA_CHN = CFG_DMA_1;

static UINT16   nofLedUsed = 0;
static color_t  led[SIZE_LED];
static UINT16   ledData[sizeof(led)*8]; // number of Bits (not byte)

static UINT16   value0Bit;
static UINT16   value1Bit;


/*******************************************************************************
 *  private functions
 */

/* converts the LED color into data for the PWM module.
 *
 */
void convColor2Data(UINT16 ledNbr)
{
    UINT16 i_color;
    UINT16 i_bit;
    UINT8 actBit;
    UINT8 *pled = (UINT8 *)(&(led[ledNbr]));
    UINT16 dataNbr = ((ledNbr*sizeof(color_t))<<3); // <<3 = multiply by 8
    for(i_color = 0; i_color < sizeof(color_t); i_color++)
    {
        for(i_bit = 0; i_bit < 8;i_bit++)
        {
            actBit = (pled[i_color] << i_bit) & 0x80; // get msb bit  first, lsb at the end
            ledData[dataNbr] = (actBit == 0x80 ? value1Bit : value0Bit);
            dataNbr++;
        }
    }   
}

/*******************************************************************************
 *  public variables (defined by external)
 */

/*******************************************************************************
 *  public functions
 */

BOOL drvLed_init                (UINT16 nofLeds)
{
    BOOL successful = (nofLeds <= DRVLED_MAX_LED);
    if(successful)
    {
        // Configure the Output Compare channels for PWM mode using Timer2
        // setup output compare channel #3 - RD2
        OpenOC3(OC_ON | OC_TIMER2_SRC | OC_PWM_FAULT_PIN_DISABLE, 0, 0);

        // set DutyCylce = 0%
        SetDCOC3PWM(0);

        // prepare dutycycles
        nofLedUsed = nofLeds;
        drvTmr2_on();
        value0Bit = drvTmr2_getCntTarget() / 3; // get value of DutyCycle for a 0-Bit
        value1Bit = 2*value0Bit;                // get value of DutyCycle for a 1-Bit
        drvTmr2_off();
        
        // clear data
        memset(ledData,0, sizeof(ledData));
    }

    return successful;
}


BOOL drvLed_setAll              (color_t color)
{
    BOOL successful = TRUE;
    UINT16 i;
    for(i = 0; i < nofLedUsed; i++)
    {
        led[i] = color;
        convColor2Data(i);
    }

    return successful;
}


BOOL drvLed_set                 (UINT16 ledNbr, color_t color)
{
    BOOL successful = (ledNbr < nofLedUsed);
    if(successful)
    {
        led[ledNbr] = color;
        convColor2Data(ledNbr);
    }

    return successful;
}


BOOL drvLed_get                 (UINT16 ledNbr, color_t *outColor)
{
    BOOL successful = TRUE;
    *outColor = led[ledNbr];

    return successful;
}


BOOL drvLed_refresh             (void)
{
    BOOL successful = TRUE;

    // Open the desired DMA channel.
    // We enable the AUTO option, we'll keep repeating the same transfer over and over.
    DmaChnOpen(DMA_CHN, 0, DMA_OPEN_ENABLE);

    // Set the transfer parameters: source & destination address, source & destination size, number of bytes per event
    int dataSize = sizeof(color_t)*8*sizeof(ledData[0])*(nofLedUsed+NOF_LED_EMPTYLEDS); // *8 = every color has 8 bit --> color * 8
    unsigned short * pDmaSrc = (void*) ledData;
    unsigned short * pDmaDst = (void*) &OC3RS;
    DmaChnSetTxfer( DMA_CHN,
                    pDmaSrc,
                    pDmaDst,
                    dataSize,
                    sizeof(ledData[0]),
                    sizeof(ledData[0]));

    // Set the transfer event control: what event is to start the DMA transfer
    DmaChnSetEventControl(DMA_CHN, DMA_EV_START_IRQ(_TIMER_2_IRQ));

    // Once we configured the DMA channel we can enable it
    // Now it's ready and waiting for an event to occur...
    DmaChnEnable(DMA_CHN);

    drvTmr2_reset();

    return successful;
}